package com.tzy.spi.core;

import com.tzy.spi.annotation.Adaptive;
import com.tzy.spi.annotation.Inject;
import com.tzy.spi.annotation.MetaObj;
import com.tzy.spi.annotation.SPI;
import com.tzy.spi.core.cache.CacheBus;
import com.tzy.spi.enhancer.Enhancer;
import com.tzy.spi.exception.AdaptiveException;
import com.tzy.spi.exception.CreateBeanException;
import com.tzy.spi.exception.LoadClassException;
import com.tzy.spi.lifecycle.AfterInject;

import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class SpiBeanHolder<T> implements ResourceLoader<T>{

    private Class<T> interfaceClass;
    private String path;
    private ClassLoader loader;

    private volatile Map<String, Class<T>> ClassMap;

    private Map<String, Class<T>> wrapperClassMap = new ConcurrentHashMap<>();

    //成型的bean
    private Map<String, BeanHolder> beanMap = new ConcurrentHashMap<>(64);

    private Map<String, InitBeanExecutor> initialBeans = new HashMap<>(32);

    private Map<String, Object> tempCacheBeans = new HashMap<>(32);

    private volatile T adaptiveBean;

    private String DEFAULT_NAME;

    private InterfaceInfo<T> interfaceInfo;

    private CacheBus bus;

    protected SpiBeanHolder(Class<T> interfaceClass, String path, ClassLoader loader, CacheBus bus) {
        this.interfaceClass = interfaceClass;
        this.path = path;
        this.loader = loader;
        this.bus = bus;
    }

    public InterfaceInfo<T> getInterfaceInfo() {

        if (this.interfaceClass == null) {
            throw new LoadClassException("没有指定接口");
        }


        if (interfaceInfo == null) {
            synchronized (interfaceClass) {
                if (interfaceInfo == null) {
                    this.interfaceInfo = parseInterface();
                }
            }
        }

        return this.interfaceInfo;
    }


    private InterfaceInfo<T> parseInterface() {


        SPI spi = interfaceClass.getAnnotation(SPI.class);

        //解析默认name
        if (spi == null || spi.value().equals("")) {
            DEFAULT_NAME = null;
        } else {
            DEFAULT_NAME = spi.value();
        }

        Method[] methods = interfaceClass.getDeclaredMethods();

        InterfaceInfo<T> info = new InterfaceInfo<T>();
        info.setDefaultName(DEFAULT_NAME);
        info.setInterfaceClass(interfaceClass);
        info.setInterfaceName(interfaceClass.getName());

        for (Method method : methods) {

            //检查每个方法上是否有adaptive
            Adaptive adaptive = method.getAnnotation(Adaptive.class);

            if (adaptive != null) {
                String value = adaptive.value();

                if (value.equals("")) {
                    throw new AdaptiveException(method.getName() + " 没有指定 adaptive 表达式");
                }

                Parameter[] parameters = method.getParameters();

                int metaObjIndex = -1;
//                String name = null;
                for (int i = 0; i < parameters.length; i++) {

                    Parameter parameter = parameters[i];

                    if (parameter.getAnnotation(MetaObj.class) != null) {
                        metaObjIndex = i;
                        break;
                    }
                }
                if (metaObjIndex == -1) {
                    throw new AdaptiveException(method.getName() + " 没有指定 meatObj");
                }
                info.addAdaptiveMethodInfo(method.getName(), adaptive, metaObjIndex);
            }
        }

        return info;
    }


    public Map<String, Class<T>> getClassMap() {

        if (this.ClassMap == null) {
            synchronized (this) {
                if (this.ClassMap == null) {
                    this.ClassMap = parsePath();
                }
            }
        }

        return this.ClassMap;

    }

    private Map<String, Class<T>> parsePath() {
        if (path == null || interfaceClass == null) {
            throw new LoadClassException("没有指定的路径，无法加载");
        }

        String filePath = path + interfaceClass.getName();

        try {
            Enumeration<URL> resources = loader.getResources(filePath);

            ConcurrentHashMap<String, Class<T>> map = new ConcurrentHashMap<String, Class<T>>();

            while (resources.hasMoreElements()) {

                InputStream stream = resources.nextElement().openStream();
                toLoad(map, stream);
            }

            return map;
        } catch (IOException e) {
            throw new LoadClassException(e.getMessage());
        }

    }

    private void toLoad(ConcurrentHashMap<String, Class<T>> map, InputStream stream) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {

            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.startsWith("#") || line.length() <= 1 || !line.contains("=")) {//跳过注释行和不合法行
                    continue;
                }
                String[] nameAndClassName = line.split("=");
                if (nameAndClassName.length != 2) {
                    throw new LoadClassException("文件语法格式错误");
                }

                String name = nameAndClassName[0].trim();
                String className = nameAndClassName[1].trim();
                Class<?> aClass = Class.forName(className, true, this.loader);

                if (!this.interfaceClass.isAssignableFrom(aClass)) {
                    throw new LoadClassException(aClass.getName() + "不是 " + interfaceClass.getName() + " 的实现");

                } else {
                    //判断是否wrapper
//                    if (Wrapper.class.isAssignableFrom(aClass)) {
//                        wrapperClassMap.put(name, (Class<T>) aClass);
//
//                    } else {
//                        //新的可以把老的替换掉
//                        map.put(name, (Class<T>) aClass);
//                    }
                    map.put(name, (Class<T>) aClass);
                }

            }

        } catch (Exception e) {
            throw new LoadClassException(e.getMessage());
        }
    }


    public T getBean(String name, boolean wrapper) {

        //先判断是否加载配置文件
        Map<String, Class<T>> classMap = getClassMap();

        Class<T> clazz = classMap.getOrDefault(name, null);

        if (clazz == null) {
            throw new LoadClassException(name + " 没有对应的类");
        }

        BeanHolder holder = createOrGetBeanHolder(name, clazz);

        return (T) holder.get(wrapper);
    }


    public T getDefaultBean(boolean wrapper) {
        getInterfaceInfo();
        return getBean(DEFAULT_NAME, wrapper);
    }

    public T getDefaultBean() {
//        getInterfaceInfo();
        return getBean(DEFAULT_NAME, false);
    }

    private BeanHolder createOrGetBeanHolder(String name, Class<T> clazz) {

        BeanHolder holder = beanMap.get(name);

        if (holder == null) {
            synchronized (clazz) {
                holder = beanMap.get(name);
                if (holder == null) {
                    T bean;
                    BeanHolder beanHolder = new BeanHolder();
                    beanHolder.setName(name);

                    bean = (T) createBean(name, clazz, false);

                    beanHolder.setBean(bean);
                    // 从三级或者二级中获取
                    //wrapper
                    Object enhanceBean = completeEnhancement(clazz, name);

                    beanHolder.setEnhanceBean(enhanceBean);

                    beanMap.put(name, beanHolder);
                }
            }

            holder = beanMap.get(name);
        }

        return holder;
    }



    private Object completeEnhancement(Class type, String name) {

        InitBeanExecutor executor = removeFromInitialBeans(name);

        //不在
        if (executor != null) {
            return executor.todo();
        } else {
            //在临时缓存中拿
            return getFromTemp(name);
        }

    }


    private T wrapper(T bean, Class<T> clazz) {

        if (this.wrapperClassMap.size() == 0) {
            return bean;
        }

        ArrayList<Wrapper<T>> list = new ArrayList<>();

        for (Map.Entry<String, Class<T>> entry : this.wrapperClassMap.entrySet()) {

            Wrapper<T> wrapperItem = (Wrapper<T>) createBean(entry.getKey(), entry.getValue(), true);
            list.add(wrapperItem);
        }
        //构建 wrapper链
        Collections.sort(list, new Comparator<Wrapper<T>>() {
            @Override
            //order 大的在前面
            public int compare(Wrapper<T> o1, Wrapper<T> o2) {
                return o2.order() - o1.order();
            }
        });

        T chainItem = bean;

        for (int i = list.size() - 1; i >= 0; i--) {
            Wrapper<T> wrapper = list.get(i);
            wrapper.injectMeta(chainItem);
            chainItem = (T) wrapper;
        }

        return chainItem;
    }


    private Object createBean(String name, Class<T> clazz, boolean isWrapper) {
        T instance;
        try {

            instance = clazz.getConstructor().newInstance();

            //enhanceBefore
            T bean = enhanceBefore(this.interfaceClass, instance, name);


            if (!isWrapper) {
//                T finalInstance = instance;
                initialBeans.put(name, () -> enhanceAfter(this.interfaceClass, bean, name));
            }
            //依赖注入
            injectAttr(bean, clazz);

            if (AfterInject.class.isAssignableFrom(clazz)) {
                //执行生命周期钩子
                ((AfterInject) bean).afterInjectToDo();
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new CreateBeanException(name + "无法创建   " + e.getMessage());
        }

        return instance;
    }

    private T enhanceBefore(Class<T> type, T instance, String name) {
        Map<String, Enhancer> beans = SpiLoader.loadInterface(Enhancer.class).getNativeBeans();

        for (Map.Entry<String, Enhancer> entry : beans.entrySet()) {

            Enhancer enhancer = entry.getValue();

            T bean = enhancer.enhanceBeforeInit(type, instance, name);
            if (bean != null) {
                //增强替代
                instance = bean;
            }

        }

        return instance;
    }

    private T enhanceAfter(Class<T> type, T instance, String name) {
        Map<String, Enhancer> beans = SpiLoader.loadInterface(Enhancer.class).getNativeBeans();

        for (Map.Entry<String, Enhancer> entry : beans.entrySet()) {
                Enhancer enhancer = entry.getValue();
                T bean = enhancer.enhanceAfterInit(type, instance, name);
                if (bean != null) {
                    //增强替代
                    instance = bean;
                }

        }

        return instance;
    }

    //依赖注入
    private void injectAttr(T instance, Class<T> clazz) {

        //找到所有合法的set方法
        Method[] methods = clazz.getMethods();

        for (Method method : methods) {

            if (isSetMethod(method)) {//以set开头且 只有一个参数判定为set方法

                Parameter parameter = method.getParameters()[0];

                Class<?> type = parameter.getType();

                if (type.isInterface()) {

                    Inject annotation = method.getAnnotation(Inject.class);
                    String value = annotation.value();
                    Object bean = null;
                    if (value.trim().equals("")) {
                        bean = SpiLoader.loadInterface(type).getAdaptiveBean();
                    } else {
                        bean = getBeanFromBus(type, value);
                    }

                    try {
                        method.invoke(instance, bean);
                    } catch (Exception e) {
                        throw new CreateBeanException(method.getName() + "  " + e.getMessage());
                    }

                } else {
                    throw new CreateBeanException(method.getName() + "依赖的不是接口无法注入");
                }

            }
        }

    }

    protected Object getBeanFromBus(Class type, String name) {

        Object bean = null;
        synchronized (type) {
            BeanHolder holder = bus.getHolder(type, name);
            //一级没有
            if (holder == null) {
                //检查临时缓存
                bean = bus.getFromTempCacheBeans(type, name);
                if (bean == null) {
                    InitBeanExecutor executor = bus.getFromInitialBeans(type, name);
                    //初始化缓存也没有
                    if (executor == null) {
                        bean = SpiLoader.loadInterface(type).getBean(name);
                    } else {
                        //获取执行器创建bean
                        bean = executor.todo();
                        //放入临时缓存
                        bus.setToTemp(type, name, bean);
                    }
                }

            } else {
                bean = holder.getEnhanceBean();
            }
        }
        return bean;
    }


    protected void setToTemp(String key, Object value) {
        this.tempCacheBeans.put(key, value);
    }

    protected BeanHolder getHolder(String value) {
        return beanMap.get(value);
    }

    protected Object getFromTemp(String value) {
        return tempCacheBeans.get(value);
    }

    protected InitBeanExecutor removeFromInitialBeans(String value) {
        return initialBeans.remove(value);
    }


    private boolean isSetMethod(Method method) {

        Inject annotation = method.getAnnotation(Inject.class);
        String name = method.getName();
        return name.startsWith("set") && method.getParameters().length == 1 && annotation != null;
    }


    public T getBean(String name) {
        return getBean(name, true);
    }


    public T getAdaptiveBean() {
        InterfaceInfo<T> interfaceInfo = getInterfaceInfo();
        int i = interfaceInfo.adaptiveNum();
        if (i == 0) return null;

        if (adaptiveBean == null) {
            synchronized (interfaceInfo) {
                if (adaptiveBean == null) {
                    this.adaptiveBean = (T) Proxy.newProxyInstance(
                            this.loader, new Class[]{interfaceInfo.getInterfaceClass()}, interfaceInfo);
                }
            }
        }

        //创建代理类并返回
        return this.adaptiveBean;

    }

    public Map<String, T> getNativeBeans() {

        HashMap<String, T> map = new HashMap<>();

        for (Map.Entry<String, BeanHolder> entry : beanMap.entrySet()) {

            String key = entry.getKey();
            BeanHolder holder = entry.getValue();
            T bean = (T) holder.getBean();
            map.put(key, bean);
        }
        return map;
    }

    public void initAllBeans() {
        //加载ClassMap
        Map<String, Class<T>> classMap = getClassMap();

        for (Map.Entry<String, Class<T>> entry : classMap.entrySet()) {
            createOrGetBeanHolder(entry.getKey(), entry.getValue());
        }

    }
}
